/*
Copyright 2009 - 2013 Peter Krajnc, SCREENWORKER

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.compactmvc.services.async.soap
{
	import mx.rpc.AbstractOperation;
	import mx.rpc.AsyncToken;
	import mx.rpc.Responder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.InvokeEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.soap.Operation;
	import mx.rpc.soap.SOAPHeader;
	import mx.rpc.soap.WebService;
	import mx.rpc.xml.SchemaTypeRegistry;

	import org.compactmvc.interfaces.IModel;
	import org.compactmvc.patterns.Controller;
	import org.compactmvc.patterns.Model;
	import org.compactmvc.services.flex.interfaces.ISoapService;

	public class SoapService extends Model implements IModel, ISoapService
	{
		public static var NAME:String = 'SoapService';
		
		protected var _serviceControl:WebService;
		protected var _operations:Object;
		protected var _operation:Operation;
		protected var _wsdlUri:String;
		protected var _wsdlPort:String;
		protected var _serviceName:String;
		protected var _header:SOAPHeader;
		
		public function SoapService(name:String, controller:Controller)
		{
			super(name, controller);
		}
		
		override final public function registerHandler():void
		{
			_serviceControl = new WebService();
			_serviceControl.addEventListener(FaultEvent.FAULT, faultHandler, false, 0, false); 
			_serviceControl.addEventListener(ResultEvent.RESULT, resultHandler, false, 0, false);
			_serviceControl.addEventListener(InvokeEvent.INVOKE, invokeHandler, false, 0, false);
			updateServiceOperations();
		}

		protected function addOperation(name:String, resultType:Class):void
		{
			if(!_operations)
				_operations = new Object();
			
			_operation = new Operation(null, name);
			_operation.resultType = resultType;
			_operations[name] = _operation;	
			
			updateServiceOperations();
			
		}
		
		protected function removeOperation(methodName:String):void
		{
			if(!_operations)
				return;
			
			if(_operations[methodName])
				_operations[methodName] = null;
			
			updateServiceOperations();
		}
		
		protected function updateServiceOperations():void
		{
			if(!_serviceControl)
				return;
			
			//update service
			_serviceControl.operations = null;
			_serviceControl.operations = _operations;
		}
		
		/**
		 * calls the operation 
		 * @param operationName
		 * @param parameters
		 * @return 
		 * 
		 */		
		protected function callOperation(methodName:String, resultHandler:Function, faultHandler:Function, ...parameters):void
		{
			if(_serviceName)
				_serviceControl.service = _serviceName;
			
			if(!_wsdlUri)
			{
				throw new Error('You have to define the WSDL Document Uri first [wsdlUri]!');
			}
			//load WSDL If Necessary
			loadWSDLIfNecessary(_wsdlUri);
			
			if(_header)
				_serviceControl.addHeader(_header); 
			
			var operation:Operation = Operation(_serviceControl.getOperation(methodName));
			operation.arguments = parameters;
			
			var token:AsyncToken = operation.send();
			token.addResponder( new Responder(resultHandler, faultHandler) );	
		}
		/**
		 * destroys the service 
		 * 
		 */		
		override final public function removeHandler():void
		{
			
			_serviceControl.disconnect();
			_serviceControl.operations = null;
			_serviceControl.addEventListener(FaultEvent.FAULT, faultHandler); 
			_serviceControl.removeEventListener(ResultEvent.RESULT, resultHandler);
			_serviceControl.removeEventListener(InvokeEvent.INVOKE, invokeHandler);
			_serviceControl = null;
			_operations = null;
		}
		
		/**
		 * loads the wsdl file 
		 * @param url
		 * 
		 */		
		protected function loadWSDLIfNecessary(url:String):void
		{		
			if(_serviceControl.ready)
				return;
			
			//sets the port first before loading wsdl
			if(_wsdlPort)
				_serviceControl.port = _wsdlPort;
			
			_serviceControl.loadWSDL(url);
		}
		
		/**
		 * sets the soap Header
		 * @param nameSpace a unique string
		 * @param headerName the webservice method name to execute
		 * @param content
		 * 
		 */				
		protected function setHeader(nameSpace:String="http://mydomain.org/xsd", headerName:String="getToken", content:Object=null):void
		{
			if(!_serviceControl)
				return;
			
			clearHeader();
			
			var ns:Namespace = new Namespace(nameSpace);
			var qname:QName = new QName(ns.uri, headerName);
			
			var header:SOAPHeader = new SOAPHeader(qname, {});
			var xmlString:String = '<'+headerName+' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="'+nameSpace+'">';
			
			if(content)
			{
				for (var key:String in content)
				{
					xmlString += '<' + key + '>'+content[key] + '</' + key + '>';
				}
				xmlString += '</'+headerName+'>';
			}
			header.content =  new XML(xmlString);
			
			trace("header-content:" + header.content);
			_header = header;
		}
		/**
		 * destroys the header 
		 * 
		 */		
		protected function clearHeader():void
		{
			_header = null;
		}

		public function faultHandler(fault:FaultEvent):void
		{
			//overriden by subclass
		}
		
		public function resultHandler(result:ResultEvent):void
		{
			
			//overriden by subclass
		}
		
		public function invokeHandler(invoke:InvokeEvent):void
		{
			
			//overriden by subclass
		}
		
		public function setupTypes():void  
		{  
			/*
			SchemaTypeRegistry.getInstance().registerCollectionClass(new QName("http://model.mynamespace","ArrayOfCountry"),myflex.model.ArrayOfCountry);  
			SchemaTypeRegistry.getInstance().registerClass(new QName("http://model.mynamespace","Country"),myflex.model.Country);  
			...  
			...  */
		}  
		
		/**
		 * returns a given mockup object 
		 * @param mockUp
		 * 
		 */			
		protected final function mockup(mockUp:Object):void
		{ 	
			resultHandler(new ResultEvent(ResultEvent.RESULT,false, false, mockUp));
		}

		protected function get wsdlUri():String
		{
			return _wsdlUri;
		}

		protected function set wsdlUri(value:String):void
		{
			_wsdlUri = value;		
		}

		protected function get wsdlPort():String
		{
			return _wsdlPort;
		}

		protected function set wsdlPort(value:String):void
		{
			_wsdlPort = value;
			
		}

		protected function get serviceName():String
		{
			return _serviceName;
		}
		
		protected function set serviceName(value:String):void
		{
			_serviceName = value;
		}


	}
}